package sel.me.popular.library.learning.quasar;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.channels.Channel;
import co.paralleluniverse.strands.channels.Channels;

import java.util.Arrays;
import java.util.Random;

/**
 * 从Skynet测试中可以看出，Quasar的性能对比Golang还是有差距的，但是不应该达到两倍多吧，经过向Quasar作者求证才得知这个测试并没有测试出实际性能，只是测试调度开销而已。
 *
 * 因为skynet方法内部几乎没有做任何事情，只是简单的做了一个加法然后进一步的递归生成新的Fiber而已，相当于只是测试了Quasar生成并调度百万Fiber所需要的时间而已。而Java里的加法操作开销远比生成Fiber的开销要低，因此感觉整体性能不如golang(golang的coroutine是语言级别的)。
 *
 * 实际上我们在实际项目中生成的Fiber中不可能只做一下简单的加法就退出，至少要花费1ms做一些简单的事情吧，(Quasar里Fiber的调度差不多在us级别)，所以我们考虑在skynet里加一些比较耗时的操作，比如随机生成1000个整数并对其进行排序，这样Fiber里算是有了相应的性能开销，与调度的开销相比，调度的开销就可以忽略不计了。(大家可以把调度开销想象成不定积分的常数)。
 *
 * 下面我分别为两种语言了加了数组排序逻辑，并插在响应的Fiber里。
 *
 *
 *
 * 其实早在JDK1的时代，Java的线程被称为GreenThread，那个时候就已经有了Fiber，但是当时不能与操作系统实现N:M绑定，所以放弃了。现在Quasar凭借ForkJoinPool这个成熟的线程调度库。另外，如果你希望你的代码能够跑在Fiber里面，需要一个很大的前提条件，那就是你所有的库，必须是异步无阻塞的，也就说必须类似于node.js上的库，所有的逻辑都是异步回调，而自Java里基本上所有的库都是同步阻塞的，很少见到异步无阻塞的。而且得益于J2EE，以及Java上的三大框架(SSH)洗脑，大部分Java程序员都已经习惯了基于线程，线性的完成一个业务逻辑，很难让他们接受一种将逻辑割裂的异步编程模型。
 *
 * 但是随着异步无阻塞这股风气起来，以及相关的coroutine语言Golang大力推广，人们越来越知道如何更好的榨干CPU性能(让CPU避免不必要的等待，减少上下文切换)，阻塞的行为基本发生在I/O上，如果能有一个库能把所有的I/O行为都包装成异步阻塞的话，那么Quasar就会有用武之地，JVM上公认的是异步网络通信库是Netty，通过Netty基本解决了网络I/O问题，另外还有一个是文件I/O，而这个JDK7提供的NIO2就可以满足，通过AsynchronousFileChannel即可。剩下的就是如何将他们封装成更友好的API了。目前能达到生产级别的这种异步工具库，JVM上只有Vert.x3，封装了Netty4，封装了AsynchronousFileChannel，而且Vert.x官方也出了一个相对应的封装了Quasar的库vertx-sync。
 *
 * Quasar目前是由一家商业公司Parallel Universe控制着，且有自己的一套体系，包括Quasar-actor，Quasar-galaxy等各个模块，但是Quasar-core是开源的，此外Quasar自己也通过Fiber封装了很多的第三方库，目前全都在comsat这个项目里。随便找一个项目看看，你会发现其实通过Quasar的Fiber去封装第三方的同步库还是很简单的。
 * https://blog.csdn.net/qiansg123/article/details/80123051
 * https://blog.csdn.net/hj7jay/article/details/51980038
 * https://blog.csdn.net/guzhangyu12345/article/details/84666423
 *
 * @author shenenlu 2021年06月23日 下午18:00:29
 */
public class QuasarSkyNet2 {

    private static Random random = new Random();
    private static final int NUMBER_COUNT = 1000;
    private static final int RUNS = 4;
    // = 0 unbufferd, > 0 buffered ; < 0 unlimited
    private static final int BUFFER = 1000;

    private static void numberSort() {
        int[] nums = new int[NUMBER_COUNT];
        for (int i = 0; i < NUMBER_COUNT; i++)
            nums[i] = random.nextInt(NUMBER_COUNT);
        Arrays.sort(nums);
    }

    static void skynet(Channel<Long> c, long num, int size, int div) throws SuspendExecution, InterruptedException {
        if (size == 1) {
            c.send(num);
            return;
        }
        //加入排序逻辑
        numberSort();
        Channel<Long> rc = Channels.newChannel(BUFFER);
        long sum = 0L;
        for (int i = 0; i < div; i++) {
            long subNum = num + i * (size / div);
            new Fiber<>(() -> skynet(rc, subNum, size / div, div)).start();
        }
        for (int i = 0; i < div; i++)
            sum += rc.receive();
        c.send(sum);
    }

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < RUNS; i++) {
            long start = System.nanoTime();

            Channel<Long> c = Channels.newChannel(BUFFER);
            new Fiber<>(() -> skynet(c, 0, 1_000_000, 10)).start();
            long result = c.receive();

            long elapsed = (System.nanoTime() - start) / 1_000_000;
            System.out.println((i + 1) + ": " + result + " (" + elapsed + " ms)");
        }
    }

}
